from typing import Any, Dict, Optional, Union, List

from sqlalchemy import select, and_, or_
from sqlalchemy.ext.asyncio import AsyncSession

from app.models.resource import Resource, ProjectResourceAllocation, TaskResourceAllocation
from app.schemas.resource import (
    ResourceCreate, ResourceUpdate,
    ProjectResourceAllocationCreate, TaskResourceAllocationCreate,
    ResourceAllocationUpdate
)


# 资源相关服务
async def get_resource_by_id(db: AsyncSession, *, id: int) -> Optional[Resource]:
    return await db.get(Resource, id)


async def get_resources(
    db: AsyncSession, *, skip: int = 0, limit: int = 100,
    resource_type: Optional[str] = None, is_available: Optional[bool] = None
) -> List[Resource]:
    query = select(Resource)
    if resource_type:
        query = query.where(Resource.resource_type == resource_type)
    if is_available is not None:
        query = query.where(Resource.is_available == is_available)
    query = query.offset(skip).limit(limit)
    result = await db.execute(query)
    return result.scalars().all()


async def create_resource(db: AsyncSession, *, obj_in: ResourceCreate) -> Resource:
    db_obj = Resource(
        name=obj_in.name,
        description=obj_in.description,
        resource_type=obj_in.resource_type,
        cost_rate=obj_in.cost_rate,
        is_available=obj_in.is_available,
        max_capacity=obj_in.max_capacity,
        metadata=obj_in.metadata,
    )
    db.add(db_obj)
    await db.commit()
    await db.refresh(db_obj)
    return db_obj


async def update_resource(
    db: AsyncSession, *, db_obj: Resource, obj_in: Union[ResourceUpdate, Dict[str, Any]]
) -> Resource:
    if isinstance(obj_in, dict):
        update_data = obj_in
    else:
        update_data = obj_in.dict(exclude_unset=True)
    for field in update_data:
        setattr(db_obj, field, update_data[field])
    db.add(db_obj)
    await db.commit()
    await db.refresh(db_obj)
    return db_obj


async def delete_resource(db: AsyncSession, *, id: int) -> Resource:
    resource = await db.get(Resource, id)
    await db.delete(resource)
    await db.commit()
    return resource


# 项目资源分配相关服务
async def get_project_resource_allocation_by_id(
    db: AsyncSession, *, id: int
) -> Optional[ProjectResourceAllocation]:
    return await db.get(ProjectResourceAllocation, id)


async def get_project_resource_allocations(
    db: AsyncSession, *, project_id: int, skip: int = 0, limit: int = 100
) -> List[ProjectResourceAllocation]:
    query = select(ProjectResourceAllocation).where(
        ProjectResourceAllocation.project_id == project_id
    )
    query = query.offset(skip).limit(limit)
    result = await db.execute(query)
    return result.scalars().all()


async def get_resource_project_allocations(
    db: AsyncSession, *, resource_id: int, skip: int = 0, limit: int = 100
) -> List[ProjectResourceAllocation]:
    query = select(ProjectResourceAllocation).where(
        ProjectResourceAllocation.resource_id == resource_id
    )
    query = query.offset(skip).limit(limit)
    result = await db.execute(query)
    return result.scalars().all()


async def create_project_resource_allocation(
    db: AsyncSession, *, obj_in: ProjectResourceAllocationCreate
) -> ProjectResourceAllocation:
    db_obj = ProjectResourceAllocation(
        project_id=obj_in.project_id,
        resource_id=obj_in.resource_id,
        allocation_percentage=obj_in.allocation_percentage,
        start_date=obj_in.start_date,
        end_date=obj_in.end_date,
        notes=obj_in.notes,
    )
    db.add(db_obj)
    await db.commit()
    await db.refresh(db_obj)
    return db_obj


async def update_project_resource_allocation(
    db: AsyncSession, *, db_obj: ProjectResourceAllocation,
    obj_in: Union[ResourceAllocationUpdate, Dict[str, Any]]
) -> ProjectResourceAllocation:
    if isinstance(obj_in, dict):
        update_data = obj_in
    else:
        update_data = obj_in.dict(exclude_unset=True)
    for field in update_data:
        setattr(db_obj, field, update_data[field])
    db.add(db_obj)
    await db.commit()
    await db.refresh(db_obj)
    return db_obj


async def delete_project_resource_allocation(
    db: AsyncSession, *, id: int
) -> ProjectResourceAllocation:
    allocation = await db.get(ProjectResourceAllocation, id)
    await db.delete(allocation)
    await db.commit()
    return allocation


# 任务资源分配相关服务
async def get_task_resource_allocation_by_id(
    db: AsyncSession, *, id: int
) -> Optional[TaskResourceAllocation]:
    return await db.get(TaskResourceAllocation, id)


async def get_task_resource_allocations(
    db: AsyncSession, *, task_id: int, skip: int = 0, limit: int = 100
) -> List[TaskResourceAllocation]:
    query = select(TaskResourceAllocation).where(
        TaskResourceAllocation.task_id == task_id
    )
    query = query.offset(skip).limit(limit)
    result = await db.execute(query)
    return result.scalars().all()


async def get_resource_task_allocations(
    db: AsyncSession, *, resource_id: int, skip: int = 0, limit: int = 100
) -> List[TaskResourceAllocation]:
    query = select(TaskResourceAllocation).where(
        TaskResourceAllocation.resource_id == resource_id
    )
    query = query.offset(skip).limit(limit)
    result = await db.execute(query)
    return result.scalars().all()


async def create_task_resource_allocation(
    db: AsyncSession, *, obj_in: TaskResourceAllocationCreate
) -> TaskResourceAllocation:
    db_obj = TaskResourceAllocation(
        task_id=obj_in.task_id,
        resource_id=obj_in.resource_id,
        allocation_percentage=obj_in.allocation_percentage,
        start_date=obj_in.start_date,
        end_date=obj_in.end_date,
        notes=obj_in.notes,
    )
    db.add(db_obj)
    await db.commit()
    await db.refresh(db_obj)
    return db_obj


async def update_task_resource_allocation(
    db: AsyncSession, *, db_obj: TaskResourceAllocation,
    obj_in: Union[ResourceAllocationUpdate, Dict[str, Any]]
) -> TaskResourceAllocation:
    if isinstance(obj_in, dict):
        update_data = obj_in
    else:
        update_data = obj_in.dict(exclude_unset=True)
    for field in update_data:
        setattr(db_obj, field, update_data[field])
    db.add(db_obj)
    await db.commit()
    await db.refresh(db_obj)
    return db_obj


async def delete_task_resource_allocation(
    db: AsyncSession, *, id: int
) -> TaskResourceAllocation:
    allocation = await db.get(TaskResourceAllocation, id)
    await db.delete(allocation)
    await db.commit()
    return allocation